#include <threadpool.h>

std::shared_ptr<threadpool> threadpool::_global = nullptr;

threadpool::threadpool(std::size_t size) : /*_threshold(size * 8),*/ _size(size), _stop(false), _idle(0) 
{
    for (std::size_t i = 0; i < _size; i++) {
#if defined(__cpp_lib_make_unique) && __cpp_lib_make_unique >= 201304L
        _threads.emplace_back(std::make_unique<std::thread>(&threadpool::worker, this));
#else // defined(__cpp_lib_make_unique) && __cpp_lib_make_unique >= 201304L
        _threads.emplace_back(new std::thread(&threadpool::worker, this));
#endif // defined(__cpp_lib_make_unique) && __cpp_lib_make_unique >= 201304L
        _idle++;
    }
}

threadpool::~threadpool() 
{
    _stop = true;
    _cv.notify_all();
    for (std::unique_ptr<std::thread> &thread : _threads) {
        if (thread->joinable()) {
            thread->join();
        }
    }
}

//void threadpool::threshold(std::size_t threshold) {
//    std::size_t minimum = std::max(2u, std::thread::hardware_concurrency());
//    _threshold = std::max(threshold, minimum);
//}
//
//std::size_t threadpool::threshold() {
//    return _threshold;
//}

std::size_t threadpool::size() const 
{
    return _size;
}

std::size_t threadpool::idle() const 
{
    return _idle;
}

std::size_t threadpool::busy() const 
{
    return _size - _idle;
}

std::shared_ptr<threadpool> threadpool::global() 
{
    static std::once_flag flag;
    std::call_once(flag, []() {
        _global = std::make_shared<threadpool>();
    });
    return _global;
}

std::shared_ptr<threadpool> threadpool::create(std::size_t size) 
{
    return std::make_shared<threadpool>(size);
}

void threadpool::worker() 
{
   do {
        std::unique_lock<std::mutex> ul(_mutex);
        _cv.wait(ul, [this] { return _stop || !_tasks.empty(); });
        if (_stop && _tasks.empty()) {
            return;
        }

        task_t task = std::move(_tasks.front());
        _tasks.pop();
        ul.unlock();

        _idle--;
        task();
        _idle++;
   } while (true);
}

//void threadpool::daemon()
//{
//    std::size_t minimum = std::max(2u, std::thread::hardware_concurrency());
//    while (!_stop) {
//        std::size_t tasks = _tasks.size();
//        if (tasks >= _size * 4 && _size < _threshold) {
//            std::size_t current = _size;
//            _size += std::min(_size / 4, _threshold - _size);
//            for (std::size_t i = current; i < _size; ++i) {
//#if __cplusplus >= 201304L // defined(__cpp_lib_make_unique)
//                _threads.emplace_back(std::make_unique<std::thread>(&threadpool::worker, this, i));
//#else // __cplusplus >= 201304L
//                _threads.emplace_back(new std::thread(&threadpool::worker, this, i));
//#endif // !__cplusplus >= 201304L
//                _idle++;
//            }
//        }
//        else if (tasks * 8 < _size && _size > minimum) {
//            std::size_t exits = std::min(_size / 8, _size - minimum);
//            _size -= exits;
//            _idle -= exits;
//            _cv.notify_all();
//        }
//        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
//    }
//}

